package benders_decomposition;

/*----------------------------------------------------------------------------

Primal Subproblem:

    min t = x1 + x2 + x3 + x4 + x5

    s.t.    x1 + x4 + x5 = 8
            x2 + x5 = 3
            x3 + x4 = 5
            x1 <= 8y1
            x2 <= 3y2
            x3 <= 5y3
            x4 <= 5y4
            x5 <= 3y5
            x >= 0

Dual Subproblem:

    max 8*a1 + 3*a2 + 5*a3 + 8*y1*b1 + 3*y2*b2 + 5*y3*b3 + 5*y4*b4 + 3*y5*b5

    s.t.    a1 + b1 <= 1
            a2 + b2 <= 1
            a3 + b3 <= 1
            a1 + a3 + b4 <= 1
            a1 + a2 + b5 <= 1
            a free
            b <= 0

----------------------------------------------------------------------------*/

import gurobi.GRB;
import gurobi.GRBEnv;
import gurobi.GRBException;
import gurobi.GRBLinExpr;
import gurobi.GRBModel;
import gurobi.GRBVar;

import Jama.Matrix;

public class DualSubproblem {

    private GRBEnv env;
    private GRBModel model;

    public int xNum = 8;
    public Matrix solutions;
    public double objVal;
    public int status;

    DualSubproblem(Matrix y) {
        try {

            env = new GRBEnv(true);
            env.start();
            model = new GRBModel(env);
            model.set("OutputFlag", "0");
            model.set("InfUnbdInfo", "1");

            // 创建决策变量
            GRBVar[] x = model.addVars(xNum, GRB.CONTINUOUS);
            for (int i = 0; i < 3; i++) {
                x[i].set(GRB.DoubleAttr.LB, -1 * GRB.INFINITY);
            }
            for (int i = 3; i < xNum; i++) {
                x[i].set(GRB.DoubleAttr.LB, -1 * GRB.INFINITY);
                x[i].set(GRB.DoubleAttr.UB, 0);
            }

            // 设置目标函数
            GRBLinExpr expr = new GRBLinExpr();
            expr.addTerms(new double[] { 8, 3, 5 }, new GRBVar[] { x[0], x[1], x[2] });
            Matrix temp = new Matrix(new double[] { 8, 3, 5, 5, 3 }, 1);
            for (int i = 0; i < 5; i++) {
                temp.set(0, i, temp.get(0, i) * y.get(i, 0));
            }
            expr.addTerms(temp.getArray()[0], new GRBVar[] { x[3], x[4], x[5], x[6], x[7] });
            model.setObjective(expr, GRB.MAXIMIZE);

            // 添加约束
            expr = new GRBLinExpr();
            expr.addTerms(new double[] { 1, 0, 0, 1, 0, 0, 0, 0 }, x);
            model.addConstr(expr, GRB.LESS_EQUAL, 1, "c1");

            expr = new GRBLinExpr();
            expr.addTerms(new double[] { 0, 1, 0, 0, 1, 0, 0, 0 }, x);
            model.addConstr(expr, GRB.LESS_EQUAL, 1, "c2");

            expr = new GRBLinExpr();
            expr.addTerms(new double[] { 0, 0, 1, 0, 0, 1, 0, 0 }, x);
            model.addConstr(expr, GRB.LESS_EQUAL, 1, "c3");

            expr = new GRBLinExpr();
            expr.addTerms(new double[] { 1, 0, 1, 0, 0, 0, 1, 0 }, x);
            model.addConstr(expr, GRB.LESS_EQUAL, 1, "c4");

            expr = new GRBLinExpr();
            expr.addTerms(new double[] { 1, 1, 0, 0, 0, 0, 0, 1 }, x);
            model.addConstr(expr, GRB.LESS_EQUAL, 1, "c5");

        } catch (GRBException e) {
            System.out.println("Error code: " + e.getErrorCode() + ". " + e.getMessage());
        }
    }

    public void solve() {
        try {

            // 求解模型
            model.optimize();

            // 设置求解情况
            status = model.get(GRB.IntAttr.Status);

            // 存储决策变量和最优值
            if (status == GRB.Status.OPTIMAL) {
                objVal = model.get(GRB.DoubleAttr.ObjVal);
                solutions = new Matrix(xNum, 1, 0);
                for (int i = 0; i < xNum; i++) {
                    solutions.set(i, 0, model.getVars()[i].get(GRB.DoubleAttr.X));
                }
            } else if (status == GRB.Status.UNBOUNDED) {
                solutions = new Matrix(xNum, 1, 0);
                for (int i = 0; i < xNum; i++) {
                    solutions.set(i, 0, model.getVars()[i].get(GRB.DoubleAttr.UnbdRay));
                }
            }

            model.dispose();
            env.dispose();

        } catch (GRBException e) {
            System.out.println("Error code: " + e.getErrorCode() + ". " + e.getMessage());
        }
    }
}
